﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media.Imaging;
using System.Windows;
using System.Diagnostics;

namespace SilverTile
{
    [DebuggerDisplay("Sprite ({X},{Y}),({Width},{Height})")]
    public class Sprite : Renderable, ISprite
    {

        /// <summary>
        /// Instansiates a sprite with the given width and height
        /// </summary>
        /// <param name="width"></param>
        /// <param name="height"></param>
        public Sprite(int width, int height)
            : base(width, height)
        {
            _frame = new Frame(width, height);
        }

        /// <summary>
        /// Instansiate a sprite from the given image frame
        /// </summary>
        /// <param name="imageFrame"></param>
        public Sprite(IFrame imageFrame)
            : base(imageFrame)
        { }

        public Sprite(string uri)
            : base(new Frame(uri))
        { }

        public Sprite(Uri uri)
            : base(new Frame(uri))
        { }


        public int[] Pixels
        {
            get { return _frame.Pixels; }
        }

        public virtual int X { get; set; }
        public virtual int Y { get; set; }
        public bool Blocking { get; set; }

        public override void Render()
        {

            if (X + Width >= 0 && Y + Height >= 0 && X < Viewport.Width && Y < Viewport.Height) // There is some part of the tile that will be visible
            {

                int xfrom = 0;
                int xto = Width;

                // Check x position if we are partially outside the area
                if (X < 0)
                {
                    // To the left
                    // set min x position
                    xfrom = -X;
                }

                if (X + Width > Viewport.Width)
                {
                    // To the right
                    // set max x position
                    xto = Viewport.Width - X;
                }

                int yfrom = 0;
                int yto = Height;

                if (Y < 0)
                {
                    // Above
                    // Set start row
                    yfrom = -Y;
                }

                if (Y + Height > Viewport.Height)
                {
                    // Below
                    // set end row
                    yto = Viewport.Height - Y;
                }

                for (int row = yfrom; row < yto; row++)
                {
                    Viewport.RenderTileRowOverwrite(X, Y, xfrom, xto, row, Width, Pixels);
                }

            }

        }

        public override void Update(TimeSpan timeSinceLastFrame)
        { }

        public IViewport Viewport { get; set; }

        public Collision CollidesWithDir(ISprite otherSprite)
        {
            return CollidesWithDir(otherSprite, X, Y, 0);
        }

        public Collision CollidesWithDir(ISprite otherSprite, int expandBy)
        {
            return CollidesWithDir(otherSprite, X, Y, expandBy);
        }

        public Collision CollidesWithDir(ISprite otherSprite, int x, int y, int expandBy)
        {
            if (!CollidesWith(otherSprite, x, y, expandBy))
            {
                return Collision.None;

            }


            Rect thisRect = new Rect(new Point(x - expandBy, y - expandBy), new Size(Width + expandBy * 2, Height + expandBy * 2));
            Rect origRect = thisRect;
            Rect otherRect = new Rect(new Point(otherSprite.X - expandBy, otherSprite.Y - expandBy), new Size(otherSprite.Width + expandBy * 2, otherSprite.Height + expandBy * 2));

            Collision collision = Collision.None;

            Point ul = new Point(origRect.Left, origRect.Top);
            Point ur = new Point(origRect.Right, origRect.Top);
            Point bl = new Point(origRect.Left, origRect.Bottom);
            Point br = new Point(origRect.Right, origRect.Bottom);

            if (otherRect.Contains(ul))
            {
                collision = collision.Set(Collision.Top).Set(Collision.Left);
            }

            if (otherRect.Contains(ur))
            {
                collision = collision.Set(Collision.Top).Set(Collision.Right);
            }

            if (otherRect.Contains(bl))
            {
                collision = collision.Set(Collision.Bottom).Set(Collision.Left);
            }

            if (otherRect.Contains(br))
            {
                collision = collision.Set(Collision.Bottom).Set(Collision.Right);
            }

            Point oul = new Point(otherRect.Left, otherRect.Top);
            Point our = new Point(otherRect.Right, otherRect.Top);
            Point obl = new Point(otherRect.Left, otherRect.Bottom);
            Point obr = new Point(otherRect.Right, otherRect.Bottom);

            if (!collision.IsSet(Collision.Top) && !origRect.Contains(oul) && !origRect.Contains(our) && origRect.Contains(obl) && origRect.Contains(obr))
            {
                collision = collision.Set(Collision.Top);
            }

            if (!collision.IsSet(Collision.Bottom) && origRect.Contains(oul) && origRect.Contains(our) && !origRect.Contains(obl) && !origRect.Contains(obr))
            {
                collision = collision.Set(Collision.Bottom);
            }

            if (!collision.IsSet(Collision.Right) && origRect.Contains(oul) && !origRect.Contains(our) && origRect.Contains(obl) && !origRect.Contains(obr))
            {
                collision = collision.Set(Collision.Right);
            }

            if (!collision.IsSet(Collision.Left) && !origRect.Contains(oul) && origRect.Contains(our) && !origRect.Contains(obl) && origRect.Contains(obr))
            {
                collision = collision.Set(Collision.Left);
            }

            collision = collision.Clear(Collision.None);

            return collision;



        }

        public bool CollidesWith(ISprite otherSprite)
        {
            return CollidesWith(otherSprite, X, Y, 0);
        }

        public bool CollidesWith(ISprite otherSprite, int x, int y)
        {
            return CollidesWith(otherSprite, x, y, 0);
        }

        public bool CollidesWith(ISprite otherSprite, int x, int y, int expandBy)
        {

            Rect thisRect = new Rect(new Point(x - expandBy, y - expandBy), new Size(Width + expandBy * 2, Height + expandBy * 2));
            Rect otherRect = new Rect(new Point(otherSprite.X - expandBy, otherSprite.Y - expandBy), new Size(otherSprite.Width + expandBy * 2, otherSprite.Height + expandBy * 2));

            thisRect.Intersect(otherRect);

            return !thisRect.IsEmpty;
        }
    }
}
